//	-*- Mode: Javascript; tab-width: 4; indent-tabs-mode: tab; c-basic-offset: 2 -*-
/*******************************************************************************
	(C)2012+ Contributors of fbwm on Google-Code.
	Authors:
		Charlie Ewing
		TriMoon <http://claimid.com/trimoon>

	This work is licensed under a Creative Commons License !
	See: http://creativecommons.org/licenses/by-nc-nd/3.0/

	See also:
		http://wiki.greasespot.net/Metadata_Block
		http://www.greywyvern.com/code/php/binary2base64
		http://www.loc.gov/standards/iso639-2/php/code_list.php
*******************************************************************************/
// ==UserScript==
// @name			Wall Manager3 Sidekick (<game name here>)
// @description		Assists Wall Manager with <game name here> posts
// @namespace		GC/fbwm
// @version			3.0.0.0 <change this version>
// @updateURL		https://fbwm.googlecode.com/hg/sidekicks/<game name here>.user.js
// @downloadURL		https://fbwm.googlecode.com/hg/sidekicks/<game name here>.user.js
// @icon			https://fbwm.googlecode.com/hg/icons/<game name here>.png
// @include			/^https?:\/\/www\.facebook\.com\/pages\/FB-Wall-Manager\//
// @include			/^<RegExp of application's reward page's URL on FB>\?/
// @include			/^<RegExp of application's reward page's URL on game server>\?/
// @require			../main/wmLibrary.js
// @grant			GM_log
// @grant			GM_getValue
// @grant			GM_setValue
// @grant			unsafeWindow
// @license			http://creativecommons.org/licenses/by-nc-nd/3.0/
// @copyright		contributors
// ==/UserScript==

/* Notes******************************************************************
	Do NOT remove the @require line for wmLibrary.
	Those parts are very much needed to make this sidekick work without
	you having to rewrite the majority of its functions.

	Do NOT remove the @include for the launch page:
	http://www.facebook.com/pages/FB-Wall-Manager/*
	or communication with the WM-host will be destroyed
*********************************************************************** */

(function() {

	//special events for window.top but not during docking
	//REVISED 2012.10.09
	if ((window.top==window.self) && !window.location.href.match( /(^http:\/\/www\.facebook\.com\/pages\/FB-Wall-Manager\/)/ )) {
		
		//This line causes the top window to listen for commands from WM's sidekick handler
		if (window.top==window.self) Sidekick.listen();
		//clean up the handler channel when page is unloaded
		window.addEventListener("onBeforeUnload", Sidekick.unlisten, false);	

		//prevent reading data from top page because it does not contain useful information and can trick the sidekick
		//remove this line if the top page is actually the page you need to read from
		//comment this line out if you:
		//	A) do not get your accept/fail messages from an iframe, or 
		//	B) the game ticker does not affect you or draw to your collection pages.
		return; 
	}

	//predefine some variables so we can just call them when needed instead of typing the same stuff over and over again
	var version = "<version here>"; // See the README.txt in this directory about Version numbering.
	var appID="<app id here>"; //find your appID on facebook or use google
	var appName="<app name here>"; //the text name of the app/game you are collecting for
	var appIcon="<app icon url here>"; // Use the same as for @icon above in meta-block.
	var appThumbsSource="http://platform.ak.fbcdn.net/www/app_full_proxy.php?app=<app id here>";

	//option to report WM2 statuses to the address bar, allowing backward
	//support for WM2 when WM3 is released. This may cause errors in chrome.
	//ADDED 2012.10.09
	var supportWM2=false;

	//attempt to dock with the WM host script
	function dock(){
	
		//enter material names here as objects
		//each object takes a name, id and event
		//id defaults to lowercase name minus spaces
		//event defaults to "Unsorted"
		//flags defaults to null
		var materials = [
			//creates a material called "Clue" found by searching for "clue", listed under "unsorted"
			{name:"clue"}, 

			//creates a material called "Nail Gun", found by searching for "nailgun", listed under "unsorted"
			{name:"nail gun", id:"nailgun"},

			//creates a material named "Spackle", found by searching for "spackle", listed under "basics"
			{name:"spackle", event:"basics"}, 

			//this material can be later searched for using the 
			//matchByParam function and pulling only items where 
			//flags matches
			//see more about this on the wiki
			{name:"xp", flags:"basics"},
			
			//just to recap: 
			//The name is the visual name of the
			//material, both in posts, and in your menu. The menu
			//will automatically capitalize the first letter of
			//every word.
			//The id is NOT the id of an item UNLESS you use the
			//id field. That means if you omit the field, the id
			//of an item is the NAME field in lower case minus spaces.
			//If you DO supply an id field, the searches will use it
			//as the text by which your material is identified. For
			//instance, if you want to identify blueberries, but the
			//post says fruit, you then specify an id of "fruit"
			//not blueberries, in which case the internal ID of your
			//item is ALSO "fruit" not "blueberries".
			//If you don't like that scenario, find information
			//regarding my flags for enforcing menu NAME fields
			//as internal id's.
			//If you supply no event field, the script will put
			//that material in an "Unsorted" group.
			//All other fields are optional, and you may use them
			//however you want.
		];

		//mark all these as new while building the menus
		//these items will appear highlighted green in the options menu
		//this sidekick tutorial has but one color for new items. 
		//if you need more colors, ask for DDM's color handler to be cloned to your sidekick
		var newItems=[
			"sendnailgun",
		];

		//these words generally denote a send type bonus
		//modify this list to fit your needs
		//not all sidekicks will want to differentiate between send and get type bonuses, but I built this for FrontierVille
		//which had daily limits of two sorts so I needed to differentiate
		var sendWords = ["needs","send","looking for","get one too","get some too","could sure use some","want to get","You'll get a","envíale","envoie-leur"];

		//manage accept texts here, which are true text names of your bonuses
		var accTextGet={}, accTextSend={};
		//by default, this sidekick will have text for both sent and collected items by name
		accTextGet = accTextFromData(materials); //omit this line if you will only be sending stuff
		accTextSend = accTextFromData(materials,"send"); //omit this line if you will only be getting stuff
		
		//if you need to enforce the NAME field of your material when creating
		//internal ID's do this instead:
		accTextGet = accTextFromData(materials,"","",MENU_ID_ENFORCE_NAME); 
		//there is more to using that MENU_ID_ENFORCE_NAME flag
		//so if you need help with something, just contact me
		
		//enter below anything you want special, something changed, or to correct text messed up by the game creators
		var accTextCustoms = {
			doUnknown:"Unknown", //needed if your script contains the menu item "doUnknown" (which is added by default now by WM host)
			send:"Unknown", //needed if your script returns just "send" without a material name and "sendall" is used in the menu

			//add any other custom texts you want here, or rewrite texts generated above in accTextGet or accTextSend
			//example: 
			xp:"XP", //instead of something accTextGet might contain such as xp:"Xp". Note the case difference.

			//here are the other entries for menu items below. This is the kind of thing this sidekick will generate automatically
			coins:"Coins", 
			energy:"Energy", 
			
			// You do NOT need to enter all the stuff that accTextFromData
			// above will automatically create for you
		};
		
		//get a list of search terms from the materials object. These terms are built specifically for text searches
		//there is no need to worry about case because the searchable portion and the search terms are all converted to lower case in the WM host
		var searchList = searchFromData(materials); //pulls the id||name field from the materials object above to use as a search term
		//var searchListNoSpaces = searchList.noSpaces(); //this version contains no spaces in each element

		//manage tests here. tests are the actual search commands performed on post data to determine what they give
		//there are two forms by which you can make tests
		
		//Form1: {<search_where>:<find_what>, ret:<return_value>} 
		//		OR find multiple terms at once {<search_where>:[<find_what>,<find_what>], ret:<return_value>}
		//Form2: {search:<search_where>, find:<find_what>, ret:<return_value>} 
		//		OR find multiple terms at once from multiple places {search:[<search_where>,<search_where>,...], find:[<find_what>,<find_what>,...], ret:<return_value>}
		//See wiki for many more options
		var tests=[
			//catch stuff that will be missed by material name searches here
			{search:["url"], find:["VRL_ASK_ENERGY"], ret:"sendenergy"}, //this searches in the url of a bonus link to find some special code
			{search:["url"], find:["VRL_HIGH_SCORE"], ret:"energy"},

			//if send words are found, but material does not exist in the list, returns just "send" so sendall picks it up
			//note that this is a tested test where subtests take presidence over return values given by the parent test
			//what that means is that if the inner tests fail, the outer test still returns "send" because something in sendWords[] was found
			{search:["link","body"], find:sendWords, ret:"send", kids:[
				//otherwise return the actual material name prefixed with "send". Example: "sendnailgun"
				{search:["url"], find:"{%1}", ret:"send{%1}", subTests:searchList}, //search first in the url
				{search:["link","body"], find:"{%1}", ret:"send{%1}", subTests:searchList}, //search second in the post link and body
			]},

			//check for getting of materials by id
			//this method is based on your above materials list
			{search:["url"], find:"{%1}",ret:"{%1}",subTests:searchList}, //search first in the url
			{search:["link","body"], find:"{%1}",ret:"{%1}",subTests:searchList}, //search second in the post link and body

			//you will want to change the previous send and get tests to fit your specific needs
			//make use of the link, body, title, and html searches as well as the many other types documented on the wiki

			//you may want to toss in some link text catch alls for stuff missed by previous searches
			//below are several examples using the old Form-1 search/test shortform for link searches
			//see the wiki for more details about the old Form-1 methods
			{link:["xp"], ret:"xp"}, //get xp
			{link:["coins","monedas","pièces","münzen"], ret:"coins"}, //get coins
			{link:["clue","indice","pista"], ret:"clue"}, //get clues
			{link:["energ"], ret:"energy"}, //get energy

		];

		//manage your menu here
		//this menu is attached to the options menu already available in the WM host
		//future versions of WM may attach your menu segment in another location
		menu={
			//this is the section housing for your sidekick which displays its name and current version
			section_main:{type:"section",label:appName+" ("+version+")",kids:{
			
				//this is a hypothetical rendering of items you could get from a friend via bonus links
				sep_GET:{type:'separator',label:'Get Items',kids:{
					coins:{type:"checkbox",label:"Coins"},
					xp:{type:"checkbox",label:"XP"},
					energy:{type:"checkbox",label:"Energy"},
					clue:{type:"checkbox",label:"Clues"},
					doUnknown:{type:"checkbox",label:"Process Unknown Links"},
				}},
				
				//this is a hypothetical rendering of items you might send a friend via bonus links
				sep_SEND:{type:"separator",label:"Send Items",kids:{
 					sendall:{type:'checkbox',label:'Send All (or select from options below)'},
					send:{type:'checkbox',label:'Send Unrecognized Items'},
				}},
			}},
		};

		//attach material lists to the menu
		//here we grab all the data out of the materials object (far) above and create a simple menu structure from it
		//automatic menu structure is in the form of GET/SEND > EVENT > NAME
		
		//use this if all your tests get something from a friend
		menuFromData(materials,menu["section_main"]["kids"]["sep_GET"]["kids"],newItems,""); //attach a list of materials to the GET block
		//use this if all your tests send something to a friend and that is how you identify them
		menuFromData(materials,menu["section_main"]["kids"]["sep_SEND"]["kids"],newItems,"send"); //attach a list of materials to the SEND block
		//OR use both of the above if you have tests that determine the difference between post types
		
		//special: menuFromData automatically uses the id||name given in the materials object above, which assumes you are identifying
		//items with the same text they might be named as, allowing you to simply omit the id or name field above
		//however, if you identify an item with the word "fruit01" but its actually a "Blueberry", then your id may not be what you want
		//listed in your menu. In that case, you can force the menuFromData function to render the name field exclusively by using a flag
		//menuFromData(materials,menu["section_main"]["kids"]["sep_GET"]["kids"],newItems,"",MENU_ID_ENFORCE_NAME);
		//for more details about this special flag, contact me

		
		//send all this information in to the WM script
		//this sidekick function relays all the information you provided here to the host script when 
		//this sidekick activates on the same page as the host
		//for details on specific parts, please see the wiki
		Sidekick.dock({
			appID:appID,
			name:appName, 
			version:version,
			thumbsSource:appThumbsSource,
			flags:{postMessageCompatible:true}, //this is required for WM3 sidekick handler and for use with Chrome
			alterLink:{},
			icon:appIcon,
			desc:null,
			accText:mergeJSON(accTextGet,accTextSend,accTextCustoms), //joins automatically generated texts with your custom texts
			tests:tests,
			menu:menu,
		});
	};

	function read(){
		try{
			var statusCode=0, text=document.documentElement.textContent;
		} catch(e){window.setTimeout(read,1000);return;} 

		/* **************************************************
		Modify this section so it catches your exact
		accept or failure texts returned by the target
		app/game

		Anything goes, as long as a status code
		is returned to the main WM host.

		Defaults are in RegExp format, but you can 
		change to simple if/then/else or switches
		if you have something better in mind

		The WM script can recieve and act on the following statusCode values:

			"2":"Responseless Collection",			//normally handled inside WM host
			"1":"Accepted",
			"0":"Unknown",
			"-1":"Failed", 					//use for reason unknown
			"-2":"None Left", 				//use for reason of already taken
			"-3":"Over Limit",
			"-4":"Over Limit, Sent One Anyway",		//registers as a type of acceptance
			"-5":"Server Error",
			"-6":"Already Got",				//registers as a type of acceptance
			"-7":"Server Down For Repairs",
			"-8":"Problem Getting Passback Link", 		//used only with special flags
			"-9":"Final Request Returned Null Page", 	//normally handled inside WM host
			"-10":"Final Request Failure",			//normally handled inside WM host
			"-11":"Expired",
			"-12":"Not a Neighbor",				//neighborship required to accept post
			"-13":"Requirements not met",			//level or building requirement
			"-15":"Unrecognized Response",			//prevent waiting for timeout if you know to watch for some known issue

			additional codes may now exist, please check the wiki support site for information
			
			NEW!: WM3 support is achieved with integer values passed directly to the host rather than text values passed via the address bar

		*************************************************** */

		//already claimed
		if (text.match(/((Y|y)ou('ve)? already (responded|claimed))/)) statusCode=-6; 
 
		//over-limits
		else if (text.match(/(cannot have anymore|reached the collection limit|maximum energy has been awarded)/)) statusCode=-3; 
 			
		//all-outs
		else if (text.match(/(all the (.*) (have|has) been claimed|no longer available)/)) statusCode=-2; 

		//generic fails
		else if (text.match(/(can't claim this reward)/))statusCode=-1;

		//expired fail
		else if (text.match(/(this reward has expired)/))statusCode=-11;

		//server errors
		else if (text.match(/(does not appear to be valid)/)) statusCode=-5;
		else if (text=="") statusCode=-5;

		//accepts
		else if (text.match(/(Gift Claimed Successfully|You just got)/)) statusCode=1;

		//here we actually pass back the status code
		//REVISED 2012.10.09
		if (statusCode!=0) {
			Sidekick.sendStatus(statusCode);
			if (supportWM2) sendMessage("status="+statusCode); //may cause cross-domain errors in chrome
		} else window.setTimeout(read,1000); //restart if nothing was found of value
		
		//alternately, if you need WM host to process a secondary link for you, then use this form
		//Sidekick.sendStatus(statusCode,link);
		//you will need to code in a portion to actually find that link for you
		//if you need help, see the PT sidekick for ideas
	};

	//start the script either as a docking sidekick, or as a reward reading sidekick
	if (window.location.href.startsWith('http://www.facebook.com/')) {
		dock(); //runs the dock function above to connect to the host
		Sidekick.openChannel(); //new WM 3 methods for communication to host
	} else read(); //runs the sidekick functions to determine post process accept/fail status

})(); // anonymous function wrapper end